[AGC003E]Sequential operations on Sequence

2020-02-12
Atcoder

题意

把序列S无限次复制,再取前$q_i$位,形成新的S

如此操作Q次,原序列是1~n,问最终每种数字在序列中出现的次数

题解

考虑倒回去操作,$k_i​$表示第i次操作后的序列在最终序列中的完整出现次数

k很好维护,考虑维护每次操作多余的部分,分散到之前的序列的k上,长度小的序列一定是它的前缀

当长度小于n的时候直接差分加答案即可

调试记录

work里没用LL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <cstdio>
#include <algorithm>
#define LL long long
const int maxn = 1e5 + 5;
using namespace std;
LL a[maxn], k[maxn], res[maxn]; int n, Q, cnt;
void work(LL x, LL v){
if (x <= a[1]){
res[1] += v;
res[x + 1] -= v;
return;
}
int t = upper_bound(a + 1, a + cnt + 1, x) - a;
k[t - 1] += v * (x / a[t - 1]);
work(x % a[t - 1], v);
}
int main(){
scanf("%d%d", &n, &Q);
a[cnt = 1] = n;
for (int i = 1; i <= Q; i++){
LL x; scanf("%lld", &x);
while (cnt > 0 && a[cnt] >= x) cnt--;
a[++cnt] = x;
}
k[cnt] = 1;
for (int i = cnt; i >= 2; i--){
k[i - 1] += k[i] * (a[i] / a[i - 1]);
work(a[i] % a[i - 1], k[i]);
}
res[0] = k[1];
res[a[1] + 1] -= k[1];
for (int i = 1; i <= n; i++){
res[i] += res[i - 1];
printf("%lld ", res[i]);
}
return 0;
}